#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#include "cdefs.h"
#include "cbin.h"
#include "cbit.h"
#include "cdes3.h"

#define FAIL(str, line) {                       \
  printf("Fail on line %d: [%s]\n", line, str); \
  return str;                                   \
}

#define ASSERT(expr) {                  \
  static_num_tests++;                   \
  if (!(expr)) FAIL(#expr, __LINE__);   \
}

#define RUN_TEST(test) {        \
    const char *msg = test();   \
    if(msg)                     \
        return msg;             \
}


static int static_num_tests = 0;

char *data[] = {
    //key              					plaintext          ciphertext
    "00000000000000000000000000000000","0000000000000000","8CA64DE9C1B123A7",
    "00000000000000000000000000000000","FFFFFFFFFFFFFFFF","355550B2150E2451",
    "01010101010101010101010101010101","0123456789ABCDEF","617B3A0CE8F07100",
    "0113B970FD34F2CE0113B970FD34F2CE","059B5E0851CF143A","86A560F10EC6D85B",
    "0123456789ABCDEF0123456789ABCDEF","0000000000000000","D5D44FF720683D0D",
    "0123456789ABCDEF0123456789ABCDEF","1111111111111111","17668DFC7292532D",
    "0131D9619DC1376E0131D9619DC1376E","5CD54CA83DEF57DA","7A389D10354BD271",
    "0170F175468FB5E60170F175468FB5E6","0756D8E0774761D2","0CD3DA020021DC09",
    "018310DC409B26D6018310DC409B26D6","1D9D5C5018F728C2","5F4C038ED12B2E41",
    "025816164629B007025816164629B007","480D39006EE762F2","A1F9915541020B56",
    "04689104C2FD3B2F04689104C2FD3B2F","26955F6835AF609A","5C513C9C4886C088",
    "04B915BA43FEB5B604B915BA43FEB5B6","42FD443059577FA2","AF37FB421F8C4095",
    "07A1133E4A0B268607A1133E4A0B2686","0248D43806F67172","868EBB51CAB4599A",
    "07A7137045DA2A1607A7137045DA2A16","3BDD119049372802","DFD64A815CAF1A0F",
    "11111111111111111111111111111111","0123456789ABCDEF","8A5AE1F81AB8F2DD",
    "11111111111111111111111111111111","1111111111111111","F40379AB9E0EC533",
    "1C587F1C13924FEF1C587F1C13924FEF","305532286D6F295A","63FAC0D034D9F793",
    "1F08260D1AC2465E1F08260D1AC2465E","6B056E18759F5CCA","EF1BF03E5DFA575A",
    "1F1F1F1F0E0E0E0E1F1F1F1F0E0E0E0E","0123456789ABCDEF","DB958605F8C8C606",
    "30000000000000003000000000000000","1000000000000001","958E6E627A05557B",
    "37D06BB516CB754637D06BB516CB7546","164D5E404F275232","0A2AEEAE3FF4AB77",
    "3849674C2602319E3849674C2602319E","51454B582DDF440A","7178876E01F19B2A",
    "43297FAD38E373FE43297FAD38E373FE","762514B829BF486A","EA676B2CB7DB2B7A",
    "49793EBC79B3258F49793EBC79B3258F","437540C8698F3CFA","6FBF1CAFCFFD0556",
    "49E95D6D4CA229BF49E95D6D4CA229BF","02FE55778117F12A","5A6B612CC26CCE4A",
    "4FB05E1515AB73A74FB05E1515AB73A7","072D43A077075292","2F22E49BAB7CA1AC",
    "584023641ABA6176584023641ABA6176","004BD6EF09176062","88BF0DB6D70DEE56",
    "7CA110454A1A6E577CA110454A1A6E57","01A1D6D039776742","690F5B0D9A26939B",
    "E0FEE0FEF1FEF1FEE0FEE0FEF1FEF1FE","0123456789ABCDEF","EDBFD1C66C29CCC7",
    "FEDCBA9876543210FEDCBA9876543210","0123456789ABCDEF","ED39D950FA74BCC4",
    "FEDCBA9876543210FEDCBA9876543210","FFFFFFFFFFFFFFFF","2A2BB008DF97C2F2",
    "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF","0000000000000000","CAAAAF4DEAF1DBAE",
    "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF","FFFFFFFFFFFFFFFF","7359B2163E4EDC58"
};

const char *test_c3des_ecb_encrypt() {
    unsigned char plaintext[8];
    unsigned char ciphertext[8];
    unsigned char key[16];
	unsigned char tmp[8];
	int i;

	for(i = 0; i < ARRAY_SIZE(data); i += 3) {
		cbin_hex_to_bin((unsigned char *)data[i], key, 32);
		cbin_hex_to_bin((unsigned char *)data[i+1], plaintext, 16);
		cbin_hex_to_bin((unsigned char *)data[i+2], ciphertext, 16);
		
		cdes3_ecb_encrypt(plaintext, tmp, key, 8);	
		ASSERT(memcmp(tmp, ciphertext, 8) == 0);
	}

    return NULL;
}

const char *test_c3des_ecb_decrypt() {
    unsigned char plaintext[8];
    unsigned char ciphertext[8];
    unsigned char key[16];
	unsigned char tmp[8];
	int i;

	for(i = 0; i < ARRAY_SIZE(data); i += 3) {
		cbin_hex_to_bin((unsigned char *)data[i], key, 32);
		cbin_hex_to_bin((unsigned char *)data[i+1], plaintext, 16);
		cbin_hex_to_bin((unsigned char *)data[i+2], ciphertext, 16);
		
		cdes3_ecb_decrypt(ciphertext, tmp, key, 8);	
		ASSERT(memcmp(tmp, plaintext, 8) == 0);
	}

    return NULL;
}


static const char *run_all_tests(void) {
    RUN_TEST(test_c3des_ecb_encrypt);
	RUN_TEST(test_c3des_ecb_decrypt);
    return NULL;
}


void mac_3des(const unsigned char *plaintext, unsigned char *ciphertext,
                      const unsigned char *key, int size) {
    unsigned char temp[8];
    int i;

    cdes3_encrypt(&plaintext[0], ciphertext, key);

    i = 8;
    while(i < size) {
        cbit_xor(&plaintext[i], ciphertext, temp, 64);
        cdes3_encrypt(temp, ciphertext, NULL);
        i = i + 8;
    }

    return;
}


int main(void) {
    const char *fail_msg = run_all_tests();
    printf("%s, tests run: %d\n", fail_msg ? "FAIL" : "PASS", static_num_tests);

	{
		unsigned char plaintext[1024];
	    unsigned char ciphertext[1024];
	    unsigned char key[16];
		
		const char *keyhex = "00000000000000000000000000000000";
		const char *plainhex = "3032303033323338343C383138303D3138303031313431343134303030303030303030303031313131363133303735353030363531343133303735353131313631343134313633333333333333333333333333333333303231303030383030393935353032303830303939353538303232323232323232323232323232323232323232323232313936323238343830313230333334303735373136303036BFD7BBA8CFE3313536";
		

		cbin_hex_to_bin((const unsigned char *)keyhex, key, 32);
		cbin_hex_to_bin((const unsigned char *)plainhex, plaintext, strlen(plainhex));

		bzero(ciphertext, sizeof(ciphertext));
		int size = strlen(plainhex) / 2;
		if(size % 8 != 0) {
			size += (8 - size % 8);
		}

		fprintf(stderr, "%d\n", size);
		mac_3des(plaintext, ciphertext, key, size);

		fprintf(stderr, "\n");
		int i;
		for(i = 0; i < 8; i++) {
			fprintf(stderr, "%x", ciphertext[i]);
		}
	}
    return fail_msg == NULL ? 0 : -1;
}

